//effect:send ntp packet and get the ntp packet ,make the time OK
//2014.7.31 is OK
//
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <netinet/udp.h>
#include <linux/if_ether.h>
#include <pthread.h>
#include <netdb.h>
#include <stddef.h>

///////////////////something for the ntp control;do by zhb //////////////////////////////////////////////
#define int8 char
#define uint8 unsigned char
#define uint32 unsigned int
#define ulong32 unsigned long
#define long32 long
#define int32 int
#define long64 long long
#define u_int32 unsigned long

#define NTP_PORT 123 /*NTP专用端口号字符串*/
#define TIME_PORT 37 /* TIME/UDP端口号 */
#define NTP_PORT_STR "123" /*NTP专用端口号字符串*/

#define NTP_SERVER_IP "192.168.1.21" /*ntp service IP*/
//3600s*24h*(365days*70years+17days)
#define JAN_1970 0x83aa7e80U // /* 1900年～1970年之间的时间秒数 */
///////////////////////////////////////////////////////////
#define PKT_MODE(li_vn_mode) ((u_char)((li_vn_mode) & 0x7))
#define PKT_VERSION(li_vn_mode) ((u_char)(((li_vn_mode) >> 3) & 0x7))
#define PKT_LEAP(li_vn_mode) ((u_char)(((li_vn_mode) >> 6) & 0x3))

//#define debug 1 
struct ntp_packet
{
uint8 li_vn_mode;
uint8 stratum;
uint8 poll;
uint8 precision;
ulong32 root_delay;
ulong32 root_dispersion;
// int8 ref_id[4];
ulong32 ref_id;
ulong32 reftimestamphigh;
ulong32 reftimestamplow;
ulong32 oritimestamphigh;
ulong32 oritimestamplow;
ulong32 recvtimestamphigh;
ulong32 recvtimestamplow;
ulong32 trantimestamphigh;
ulong32 trantimestamplow;
};
long64 firsttimestamp,finaltimestamp;

int sendpkt(int sockfd,struct addrinfo * res)
{


struct ntp_packet ntppack,newpack;

//put the date into the ntppack	
ntppack.li_vn_mode = 0x23;
ntppack.stratum = 0x02;
ntppack.poll = 0x04;
ntppack.precision = 0xec;
ntppack.root_delay = htonl(1<<16);//root_delay = 1.0sec
ntppack.root_dispersion = htonl(1<<8);//root_dispersion = 0.0039sec
ntppack.ref_id = inet_addr(NTP_SERVER_IP);
//获取初始时间戳T1
firsttimestamp=JAN_1970+time(NULL);//-8*3600
//	printf("%lx,%lx\n",JAN_1970,firsttimestamp);
ntppack.reftimestamphigh = htonl(firsttimestamp);
ntppack.oritimestamphigh = htonl(firsttimestamp);
ntppack.recvtimestamphigh = htonl(firsttimestamp);
ntppack.trantimestamphigh= htonl(firsttimestamp);

int i;
for(i=0; i<1; i++)
{
int ret = sendto(sockfd,&ntppack,sizeof(ntppack),0, res->ai_addr, res->ai_addrlen);
if(ret < 0){
perror("sendto");
return 1;
}
}

}
int getresponse(int sockfd,struct addrinfo * res,struct ntp_packet rpkt)
{
//	struct ntp_packet rpkt,newpack;
fd_set pending_data;
struct timeval block_time;
char *refid ;

/*调用select()函数，并设定超时时间为1s*/
FD_ZERO(&pending_data);
FD_SET(sockfd, &pending_data);

block_time.tv_sec=10;//how time to ask
block_time.tv_usec=0;
if (select(sockfd + 1, &pending_data, NULL, NULL, &block_time) > 0)

{
int num;
/*接收服务器端的信息*/
if ((num = recvfrom(sockfd, &rpkt,
sizeof(rpkt), 0, res->ai_addr, &res->ai_addrlen)) < 0)
{
perror("recvfrom");
return 0;
}

/* 设置接收NTP包的数据结构 */
int mode = PKT_MODE(rpkt.li_vn_mode);
int version = PKT_VERSION(rpkt.li_vn_mode);
int leap = PKT_LEAP(rpkt.li_vn_mode);

int stratum = rpkt.stratum;
int poll = rpkt.poll;
int precision = rpkt.precision;


//到达客户机时间戳T4
finaltimestamp=time(NULL)+JAN_1970;//-8*3600;
///将网络上传送的大端数据改为小端形式。
rpkt.root_dispersion= ntohl(rpkt.root_dispersion);
rpkt.reftimestamphigh=ntohl(rpkt.reftimestamphigh);
rpkt.reftimestamplow= ntohl(rpkt.reftimestamplow);
rpkt.oritimestamphigh= ntohl(rpkt.oritimestamphigh);
rpkt.oritimestamplow= ntohl(rpkt.oritimestamplow);
rpkt.recvtimestamphigh= ntohl(rpkt.recvtimestamphigh);
rpkt.recvtimestamplow= ntohl(rpkt.recvtimestamplow);
rpkt.trantimestamphigh= ntohl(rpkt.trantimestamphigh);
rpkt.trantimestamplow= ntohl(rpkt.trantimestamplow);
#ifdef debug
printf("li=%d,version=%d,mode=%d\n",leap,version,mode);
printf("stratum=%d,poll=%d,precision=%d\n",stratum,poll,precision);
printf("################ data ####################\n");
printf("root_delay=%ld\n",rpkt.root_delay);
printf("dispersion=%ld\n",rpkt.root_dispersion);
//	printf("Id=%s\n",(*(int*)&rpkt.ref_id));
printf("refh=%lx\n",rpkt.reftimestamphigh);
printf("relw=%lx\n",rpkt.reftimestamplow);
printf("orih=%lx\n",rpkt.oritimestamphigh);
printf("oril=%lx\n",rpkt.oritimestamplow);
printf("rech=%lx\n",rpkt.recvtimestamphigh);
printf("recl=%lx\n",rpkt.recvtimestamplow);
printf("trah=%lx\n",rpkt.trantimestamphigh);
printf("tral=%lx\n",rpkt.trantimestamplow);
#endif

long64 diftime,delaytime;
//求出客户机跟服务器的时间差=((T2-T1)+(T3-T4))/2
diftime=((rpkt.recvtimestamphigh-firsttimestamp)+(rpkt.trantimestamphigh-finaltimestamp))>>1;
//求出延时
delaytime=((rpkt.recvtimestamphigh-firsttimestamp)-(rpkt.trantimestamphigh-finaltimestamp))>>1;
//diftime=(5-9)>>1;

//求出真正时间的时间戳
struct timeval tv1;
tv1.tv_sec=time(NULL)+diftime+delaytime;
tv1.tv_usec=0;
settimeofday(&tv1,NULL);
#ifdef debug
printf("\n\ndebug information ...\n\n");
printf("time(NULL) is %ld\n",time(NULL));
printf("different time is %ld\n",diftime);
printf("delaytime is %ld\n",delaytime);
printf("time(NULL)+diftime+delaytime=%ld\n",time(NULL)+diftime+delaytime);
printf("tv1.tv_sec is %ld\n\n", tv1.tv_sec);
#endif
}	
return 1;
}


int main()
{

struct addrinfo *res = NULL,hints;

memset(&hints,0,sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;

/*调用getaddrinfo()函数，获取地址信息*/
int rc = getaddrinfo(NTP_SERVER_IP, NTP_PORT_STR, &hints, &res);
if (rc != 0)
{
perror("getaddrinfo");
return 1;
}

int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if(fd < 0){
perror("socket");
return 0; 
}


sendpkt(fd,res);
int i;

struct ntp_packet rpkt;
i = getresponse(fd,res,rpkt);

printf("time is OK\n");
close(fd);
}